/*
 * Copyright (c) 2009. Silenus Consultoria, S.L.
 */
package es.silenus.detecta.dao.impl;

import es.silenus.detecta.dao.SurveyDAO;

import es.silenus.detecta.model.Survey;
import es.silenus.detecta.model.SurveyLifecycle;

import org.hibernate.FetchMode;
import org.hibernate.Session;

import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;

import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;

import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import static java.util.Arrays.asList;
import java.util.List;
import java.util.Map;
import java.sql.Date;


/**
 * DOCUMENT ME!
 *
 * @author <a href="mailto:dgarcia@silenus-consultoria.es">Daniel Garc&iacute;a</a>
 *
 * @since 02-abr-2009 12:47:08
 */
public class SurveyDAOImpl extends ReadWriteDAOImpl<Survey, Integer> implements SurveyDAO {
	/**
	 * The check remove surveys SQL.
	 */
	private String checkRemoveSurveysSQL;

	/**
	 * The fetch question tree SQL.
	 */
	private String fetchQuestionTreeSQL;

	/**
	 * The answer entities.
	 */
	private List<String> answerEntities;

	/**
	 * Sets the check remove surveys SQL.
	 *
	 * @param checkRemoveSurveysSQL the check remove surveys SQL.
	 */
	public void setCheckRemoveSurveysSQL(String checkRemoveSurveysSQL) {
		this.checkRemoveSurveysSQL = checkRemoveSurveysSQL;
	}

	/**
	 * Sets the fetch question tree SQL.
	 *
	 * @param fetchQuestionTreeSQL the fetch question tree SQL.
	 */
	public void setFetchQuestionTreeSQL(final String fetchQuestionTreeSQL) {
		this.fetchQuestionTreeSQL = fetchQuestionTreeSQL;
	}

	/**
	 * Sets the answer entities.
	 *
	 * @param answerEntities the answer entities.
	 */
	public void setAnswerEntities(final List<String> answerEntities) {
		this.answerEntities = answerEntities;
	}

	/**
	 * Finds a survey by its identifier.
	 *
	 * @param id the id.
	 *
	 * @return the quest
	 */
	@SuppressWarnings("unchecked")
	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public Survey findById(final Integer id) {
		Session session = null;

		try {
			session = getSession();

			return (Survey)session.getNamedQuery(Survey.class.getName() + ".findById").setLong("id", id).uniqueResult();
		} finally {
			releaseSession(session);
		}
	}

	/**
	 * Finds a Survey by its code (unique in an organization)
	 *
	 * @param code the code.
	 * @param organizationId the organizationId.
	 *
	 * @return the training action.
	 */
	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public Survey findByCode(String code, int organizationId) {
		Session session = null;

		try {
			session = getSession();

			return (Survey)session.getNamedQuery(Survey.class.getName() + ".findByCode").setString("code", code)
														.setInteger("organizationId", organizationId).uniqueResult();
		} finally {
			releaseSession(session);
		}
	}

	/**
	 * Override this method to provide a find by example page criteria.
	 *
	 * @param criteria the criteria.
	 * @param object the sample object.
	 *
	 * @return the criteria.
	 */
	protected DetachedCriteria preparePageCriteria(final DetachedCriteria criteria, final Map<String, Object> object) {
		criteria.setFetchMode("organization", FetchMode.JOIN);
		criteria.setFetchMode("lifecycle", FetchMode.JOIN);

		Object temp = object.get("id");

		if(temp != null) {
			criteria.add(Restrictions.eq("id", temp));
		}

		temp = object.get("code");

		if(temp != null) {
			criteria.add(Restrictions.eq("code", temp));
		}

		temp = object.get("title");

		if(temp != null) {
			criteria.add(Restrictions.eq("title", temp));
		}

		temp = object.get("description");

		if(temp != null) {
			criteria.add(Restrictions.eq("description", temp));
		}

		temp = object.get("organization.id");

		if(temp != null) {
			criteria.add(Restrictions.eq("organization.id", temp));
		}

		temp = object.get("lifecycle.id");

		if(temp != null) {
			criteria.add(Restrictions.eq("lifecycle", SurveyLifecycle.getById((Integer)temp)));
		}

		return criteria;
	}

	/**
	 * Override this method to provide a find by example list criteria.
	 *
	 * @param criteria the criteria.
	 * @param object the sample object.
	 *
	 * @return the criteria.
	 */
	protected DetachedCriteria prepareListCriteria(final DetachedCriteria criteria, final Map<String, Object> object) {
		return preparePageCriteria(criteria, object);
	}

	/**
	 * Retrieves the removal information of entities with the given keys.
	 *
	 * @param keys the keys.
	 *
	 * @return the information requested.
	 */
	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public List<Map<String, Object>> getRemovalInformation(final Integer[] keys) {
		MapSqlParameterSource args = new MapSqlParameterSource();

		args.addValue("ids", asList(keys));

		return jdbcTemplate.queryForList(checkRemoveSurveysSQL, args);
	}

	/**
	 * Removes surveys by keys.
	 *
	 * @param keys the keys.
	 */
	@Transactional(propagation = Propagation.REQUIRED)
	public void remove(final Integer[] keys) {
		List<Survey> elements = findByIds(keys);

		for(Survey element : elements) {
			super.remove(element);
		}
	}

	/**
	 * Retrieves the question tree.
	 *
	 * @param surveyId the survey identifier.
	 *
	 * @return the question tree.
	 */
	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public List<Map<String, Object>> fetchQuestionTree(int surveyId) {
		MapSqlParameterSource args = new MapSqlParameterSource();

		args.addValue("id", surveyId);

		return jdbcTemplate.queryForList(fetchQuestionTreeSQL, args);
	}

	/**
	 * Clears the cache.
	 */
	public void clearCache() {
		super.clearCache();

		super.clearEntityCaches(answerEntities);
	}

	/**
	 * Retrieves active surveys by organization.
	 *
	 * @param organizationId the organization id.
	 *
	 * @return the list of active surveys.
	 */
	@SuppressWarnings("unchecked")
	@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
	public List<Survey> findActiveSurveys(int organizationId) {

		DetachedCriteria criteria = getDetachedCriteria();

		criteria.setFetchMode("organization", FetchMode.JOIN);
		criteria.setFetchMode("lifecycle", FetchMode.JOIN);

		// Filter by organization
		criteria.add(Restrictions.eq("organization.id", organizationId));

		// Filter by lifecycle activity
		Object[] lifecycles = { SurveyLifecycle.DESIGN, SurveyLifecycle.PRODUCTION };
		criteria.add(Restrictions.in("lifecycle", lifecycles));

		// Filter by close date
		Date now = new Date(System.currentTimeMillis());
		criteria.add(Restrictions.gt("endDate", now));

		Session session = null;

		try {
			session = getSession();

			return criteria.getExecutableCriteria(session).list();
		} finally {
			releaseSession(session);
		}
	}
}
